﻿#include "stdafx.h"
#include "sbcs.h"

//////////////////////////////////////////////////////////////////////////
// utf-8용 문자처리기

// 스킵테이블
static const char _k_utf8_skips[256] =
{
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
};

// 오프셋
static kssize_t _k_utf8_pointer_to_offset(const char* s, const char* p)
{
	if (p < s)
		return -_k_utf8_pointer_to_offset(p, s);
	else
	{
		const char* t = s;
		kssize_t offset = 0;

		while (t < p)
		{
			t = k_utf8_char_next(t);
			offset++;
		}

		return offset;
	}
}

// 오프셋
static char* _k_utf8_offset_to_pointer(const char* s, kssize_t offset)
{
	const char* t = s;

	if (offset > 0)
	{
		while (offset--)
			t = k_utf8_char_next(t);
	}
	else
	{
		while (offset)
		{
			const char* z = t;
			t += offset;

			while ((*t & 0xC0) == 0x80)
				t--;

			offset += _k_utf8_pointer_to_offset(t, z);
		}
	}

	return (char*)t;
}

// 이전 글자 찾기
char* k_utf8_find_char_prev(const char* s, const char* p)
{
	for (--p; p >= s; --p)
	{
		if ((*p & 0xC0) != 0x80)
			return (char*)p;
	}

	return NULL;
}

// 다음 글자 찾기
char* k_utf8_find_char_next(const char* s, const char* end)
{
	if (*s)
	{
		if (end)
		{
			for (++s; s < end && (*s & 0xC0) == 0x80; ++s)
				;
		}
		else
		{
			for (++s; (*s & 0xC0) == 0x80; ++s)
				;
		}
	}

	return s == end ? NULL : (char*)s;
}

// 이전 글자
char* k_utf8_char_prev(const char* s)
{
	for (;;)
	{
		s--;

		if ((*s & 0xC0) != 0x80)
			return (char*)s;
	}
}

// 다음 글자
char* k_utf8_char_next(const char* s)
{
	return (char*)(s + _k_utf8_skips[*(const kbyte*)s]);
}

// 글자 길이
// cp_strlen_utf8 (출처 http://www.daemonology.net/blog/2008-06-05-faster-utf8-strlen.html)
ksize_t k_utf8len(const char* s)
{
#define MASK    ((ksize_t)(-1)/0xFF)
	const char* t;
	ksize_t cnt = 0;
	ksize_t i;
	kbyte b;

	// 초기 정렬되지 않은 아무 바이트 계산
	for (t = s; K_CAST_PTR_TO_PUINT(t) & (sizeof(ksize_t)-1); t++)
	{
		b = *t;

		if (b == '\0')
			goto pos_done;

		cnt += (b >> 7) & ((~b) >> 6);
	}

	// 완전한 블럭 계산
	for (;; t += sizeof(ksize_t))
	{
#ifdef __GNUC__
		__builtin_prefetch(&t[256], 0, 0);
#endif

		i = *(ksize_t*)(t);

		if ((i - MASK) & (~i) & (MASK * 0x80))
			break;

		i = ((i & (MASK * 0x80)) >> 7) & ((~i) >> 6);
		cnt += (i * MASK) >> ((sizeof(ksize_t)-1) * 8);
	}

	//
	for (;; t++)
	{
		b = *t;

		if (b == '\0')
			break;

		cnt += (b >> 7) & ((~b) >> 6);
	}

pos_done:
	return ((t - s) - cnt);
#undef MASK
}

// 복사
char* k_utf8cpy(char* dest, const char* src)
{
	return k_strcpy(dest, src);
}

// 복사
char* k_utf8pcpy(char* dest, const char* src)
{
	return k_strpcpy(dest, src);
}

// 복사
char* k_utf8ncpy(char* dest, const char* src, ksize_t len)
{
	const char* t = src;

	while (len && *t)
	{
		t = k_utf8_char_next(t);
		len--;
	}

	len = t - src;
	k_strncpy(dest, src, len);
	dest[len] = '\0';

	return dest;
}

// 서브 스트링
char* k_utf8mid(char* dest, const char* src, ksize_t pos, ksize_t len)
{
	char* start;
	char* end;


	start = _k_utf8_offset_to_pointer(src, (kssize_t)pos);
	end = _k_utf8_offset_to_pointer(start, (kssize_t)len);

	len = end - start;
	memcpy(dest, start, len);
	dest[len] = '\0';

	return dest;
}

// strchr
char* k_utf8chr_ucs(const char* s, kucs4char ch)
{
	char sz[10];
	kint len = k_utf8cunc(ch, sz);
	sz[len] = '\0';
	return k_strstr(s, sz);
}

// strchr
char* k_utf8chr(const char* s, const char* utf8ch)
{
	return k_strstr(s, utf8ch);
}

// strrchr
char* k_utf8rchr_ucs(const char* s, kucs4char ch)
{
	char sz[10];
	kint len = k_utf8cunc(ch, sz);
	sz[len] = '\0';
	return k_strrstr(s, sz);
}

// strrchr
char* k_utf8rchr(const char* s, const char* utf8ch)
{
	return k_strrstr(s, utf8ch);
}

// strrev
char* k_utf8rev(char* s)
{
	char* res;
	char* r;
	char* m;
	char* p;
	ksize_t len;

	len = k_strlen(s);
	res = (char*)alloca(len + 1);

	r = res + len;
	p = s;

	while (r > res)
	{
		char skip = _k_utf8_skips[*(kbyte*)p];
		r -= skip;

		for (m = r; skip; skip--)
			*m++ = *p++;
	}

	res[len] = '\0';
	k_strcpy(s, res);

	return s;
}

//////////////////////////////////////////////////////////////////////////
// 문자 변환

// UTF8을 유니코드로
kucs4char k_utf8ccnc(const char* p)
{
	kint len, mask;
	kbyte ch = *(kbyte*)p;

	if (ch < 128)
	{
		len = 1;
		mask = 0x7f;
	}
	else if ((ch & 0xe0) == 0xc0)
	{
		len = 2;
		mask = 0x1f;
	}
	else if ((ch & 0xf0) == 0xe0)
	{
		len = 3;
		mask = 0x0f;
	}
	else if ((ch & 0xf8) == 0xf0)
	{
		len = 4;
		mask = 0x07;
	}
	else if ((ch & 0xfc) == 0xf8)
	{
		len = 5;
		mask = 0x03;
	}
	else if ((ch & 0xfe) == 0xfc)
	{
		len = 6;
		mask = 0x01;
	}
	else
	{
		len = -1;
		mask = 0;
	}

	if (len < 0)
	{
		// 사용하지 않는 문자 코드
		// 0xFFFFFFFF
		return (kucs4char)-1;
	}
	else
	{
		// UCS4로 변환
		kucs4char ret = p[0] & mask;
		kint i;

		for (i = 1; i < len; i++)
		{
			if ((p[i] & 0xC0) != 0x80)
			{
				ret = (kucs4char)-1;
				break;
			}

			ret <<= 6;
			ret |= (p[i] & 0x3F);
		}

		return ret;
	}
}

// 유니코드를 UTF8로, 반환값은 길이, 결과는 out에
kint k_utf8cunc(kucs4char c, char* out)
{
	kint first, len;

	if (c < 0x80)
	{
		first = 0;
		len = 1;
	}
	else if (c < 0x800)
	{
		first = 0xc0;
		len = 2;
	}
	else if (c < 0x10000)
	{
		first = 0xe0;
		len = 3;
	}
	else if (c < 0x200000)
	{
		first = 0xf0;
		len = 4;
	}
	else if (c < 0x4000000)
	{
		first = 0xf8;
		len = 5;
	}
	else
	{
		first = 0xfc;
		len = 6;
	}

	if (out)
	{
		kint i;

		for (i = len - 1; i > 0; --i)
		{
			out[i] = (c & 0x3f) | 0x80;
			c >>= 6;
		}

		out[0] = (char)(c | first);
	}

	return len;
}
